package pkg.networking;
/**
 * BomberBoy, CPS240 Term Project - Team A
 * Andrew Osterink, David Bash, Scott Kidder
 * 
 * ServerBase adds server functions to the NetworkConnection
 * it basically adds server specific functions and event notification
 * for subclasses
 */

import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;


public class ServerBase extends NetworkConnection {
	
	private ServerSocketChannel channel;
	
 	public ServerBase()
 	{
 		log = Logger.getLogger("Bomb.SeverCore");
 		log.setUseParentHandlers(true);
 		log.setLevel(null);
 	}
 	
 	public void start(int port)
 	{
 		
 		try {
 			log.info("Starting server on port " + port + " and " + InetAddress.getLocalHost());
			selector = SelectorProvider.provider().openSelector();
			channel = ServerSocketChannel.open();
		    channel.configureBlocking(false);
		    
		    InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),port);
		    channel.socket().bind(address);
		    channel.register(selector,SelectionKey.OP_ACCEPT);
		    connected = true;
		    startPump();
 		} catch (IOException e) {
 		    log.severe("Error Address already in use");
		    log.fine(e.toString());
		}
 		
 	}
 	
 	protected final void onAcceptConnection(SelectionKey key)
 	{
 		try {
 		ServerSocketChannel serverSockChannel = (ServerSocketChannel) key.channel();
 		SocketChannel socketChan = serverSockChannel.accept();
 		if(socketChan != null)
 		{
 			socketChan.configureBlocking(false);
 			log.log(Level.INFO,"Client Connected from " + socketChan.socket().getInetAddress());
 			socketChan.register(this.selector, SelectionKey.OP_READ);
 			clientConnected(socketChan);
 		}
 		} catch (IOException e) {
			log.severe("Unknown Error Accepting Connection");
			log.warning(e.toString());
		}
 	}
 	
 	protected void clientConnected(SocketChannel socket)
 	{
 		
 	}
 	
 	protected void onRead(SelectionKey key,ByteBuffer data)
 	{
 		if(data == null)
 		{
 			log.info("Error reading packet disconnecting client!");
 			disconectKey(key);
 			return;
 		}
 		
 		try {
			RemoteCommand cmd = deserializeCommand(data);
			if(onCommandReceived(cmd))
				broadcastCommand(cmd);
 		} catch (IOException e) {
 		
			log.severe("Server Error: error deserializeing packet");
			log.warning(e.fillInStackTrace().toString());
		}
 	}

 	public void broadcastBytes(ByteBuffer data)
 	{
 		for(SelectionKey key : selector.keys() )
 		{
 			if(!(key.channel() instanceof SocketChannel))
 				continue; //there are keys in hear that arnt clients skip them
 			writeBytes(key,data);
 		}
 		this.selector.wakeup();
 	}
 	
 	public void broadcastCommand(RemoteCommand cmd)
 	{
 		ByteBuffer buffer = ByteBuffer.wrap(serializeCommand(cmd).toByteArray());
 		broadcastBytes(buffer);
 	}
 	
 	
 	protected void onDisconect(SelectionKey key)
 	{
 			log.log(Level.INFO,"Client Disconected");
 	}
 	
 	/**
 	 * Called when ever the server receives a command from a client
 	 * The return controls weather or not the server should handle the data 
 	 * in its default way or not. (broadcasting packet to all)
 	 * @return true to broadcast packet or false to discard packet after function
 	 */
 	protected boolean onCommandReceived(RemoteCommand cmd)
 	{
 		return true;
 	}
}
